package cn.ygh.bbo.system.sdk.config;

import cn.ygh.bbo.framerwork.config.properties.ShiroProperties;
import cn.ygh.bbo.framerwork.redis.RedisService;
import cn.ygh.bbo.system.sdk.shiro.RedisSessionDao;
import cn.ygh.bbo.framerwork.config.constant.ShiroConstant;
import cn.ygh.bbo.system.sdk.shiro.CustomSessionManager;
import cn.ygh.bbo.system.sdk.shiro.UserRealm;
import cn.ygh.bbo.system.sdk.shiro.filter.CacheClearFilter;
import cn.ygh.bbo.system.sdk.shiro.filter.DemonstrationFilter;
import cn.ygh.bbo.system.sdk.shiro.filter.UserAuthenticationFilter;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author ygh
 * @Desc shiro 自定义注入
 * filter不要使用bean注解 不然会有两次调用的
 * @date 2019/9/29
 */
@Configuration
public class ShiroAutoConfiguration {

    @Autowired
    ShiroProperties shiroProperties;

    /**
     * Shiro过滤器配置
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 身份认证失败，则跳转到登录页面的配置
        shiroFilterFactoryBean.setLoginUrl(ShiroConstant.LOGIN_URL);
        // 权限认证失败，则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl(ShiroConstant.UNAUTHORIZED_URL);
        //自定义拦截器
        Map<String, Filter> filters=new HashMap<>();
        filters.put("authc", userAuthenticationFilter());
        filters.put("demonstration", demonstrationFilter());
        filters.put("clear", cacheClearFilter());
        shiroFilterFactoryBean.setFilters(filters);
        // Shiro连接约束配置，即过滤链的定义
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // 所有请求请求不需要认证
        filterChainDefinitionMap.put("/druid/**", "anon,clear");
        // 数据操作接口需要登陆
        filterChainDefinitionMap.put("/restpage/**", "authc,demonstration,clear");
        // 所有请求请求不需要认证
        filterChainDefinitionMap.put("/**", "anon,demonstration,clear");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 认证过滤器
     * @return
     */
    private UserAuthenticationFilter userAuthenticationFilter(){
        return new UserAuthenticationFilter();
    }


    /**
     * 缓存清理过滤器
     * @return
     */
    private CacheClearFilter cacheClearFilter(){
        return new CacheClearFilter();
    }

    /**
     * 演示过滤器
     * @return
     */
    private DemonstrationFilter demonstrationFilter(){
        return new DemonstrationFilter();
    }

    /**
     * 安全管理器
     */
    @Bean
    public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager
            , @Autowired(required = false)CacheManager cacheManager) throws IOException {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(userRealm);
        //cache
        securityManager.setCacheManager(cacheManager);
        //会话管理
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    /**
     * 会话管理器
     * @return
     */
    @Bean
    public SessionManager sessionManager(SessionDAO sessionDAO){
        SessionManager sessionManager=new CustomSessionManager();
        ((CustomSessionManager) sessionManager).setGlobalSessionTimeout(shiroProperties.getTimeOut());
        //redis不需要定时检验
        if (sessionDAO instanceof RedisSessionDao){
            ((CustomSessionManager) sessionManager).setSessionValidationSchedulerEnabled(false);
        }
        ((CustomSessionManager) sessionManager).setSessionDAO(sessionDAO);
        return sessionManager;
    }

    /**
     * 会话Dao
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(value = SessionDAO.class)
    public SessionDAO sessionDAO(){
        return new MemorySessionDAO();
    }

    /**
     * redis会话Dao
     * @return
     */
    @Bean
    @ConditionalOnBean(RedisService.class)
    public SessionDAO sessionDAO(RedisService redisService){
        RedisSessionDao redisSessionDao=new RedisSessionDao();
        redisSessionDao.setTimeOut(shiroProperties.getTimeOut());
        redisSessionDao.setRedisService(redisService);
        return redisSessionDao;
    }
    /**
     * 权限认证
     * @return
     */
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }

    /**
     * 开启Shiro注解通知器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
